package cn.anecansaitin.industrialization_expansion.common.cap;

import cn.anecansaitin.industrialization_expansion.api.common.cap.IModifiedAttribute;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
import net.minecraft.entity.ai.attributes.Attribute;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.ListNBT;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.util.Constants;
import net.minecraftforge.fml.common.thread.EffectiveSide;
import net.minecraftforge.registries.ForgeRegistries;

import javax.annotation.Nonnull;
import java.util.Objects;

/**
 * nbt结构
 * nbt
 *  └ cap
 *     └ attribute (compound)
 *        ├ latest (boolean)
 *        └ attribute (list)
 *           ├ 0 (compound)
 *           │  ├ id (string)
 *           │  └ modifier (list)
 *           │     ├ 0 (compound)
 *           │     │  ├ ┌──────────────┐
 *           │     │  ├ │   modifier   │
 *           │     │  ├ │ serializeNBT │
 *           │     │  └ └──────────────┘
 *           │     ├ 1
 *           ├ 1
 */
public class ItemModifiedAttribute implements IModifiedAttribute {
    protected Multimap<Attribute, AttributeModifier> attribute = ArrayListMultimap.create();
    protected final ItemStack stack;

    public ItemModifiedAttribute(ItemStack stack) {
        this.stack = stack;
        deserialize(getCapNBT());
    }

    @Nonnull
    @Override
    public Multimap<Attribute, AttributeModifier> getAttribute() {
        clientCheck();
        return attribute;
    }

    @Override
    public void setAttribute(Multimap<Attribute, AttributeModifier> attribute) {
        this.attribute = attribute;
        setCapNBT(serialize());
        onAttributeChanged();
    }

    @Override
    public void putAttribute(Attribute attribute, AttributeModifier modifier) {
        boolean put = this.attribute.put(attribute, modifier);

        if (!put)
            return;

        CompoundNBT cap = getCapNBT();
        ListNBT listNBT;

        if (!cap.contains("attribute")){
            listNBT = new ListNBT();
            cap.put("attribute", listNBT);
        }

        listNBT = cap.getList("attribute", Constants.NBT.TAG_COMPOUND);
        String attributeId = Objects.requireNonNull(attribute.getRegistryName()).toString();

        for (int i = 0; i < listNBT.size(); i++) {
            CompoundNBT compound = listNBT.getCompound(i);

            if (!compound.contains("id"))
                compound.putString("id", attributeId);


            if (!compound.getString("id").equals(attributeId))
                continue;

            ListNBT modifierList;

            if (!compound.contains("modifier")) {
                modifierList = new ListNBT();
                compound.put("modifier", modifierList);
            } else
                modifierList = compound.getList("modifier", Constants.NBT.TAG_LIST);

            modifierList.add(modifier.save());
        }

        onAttributeChanged();
    }

    @Override
    public void clear() {
        attribute.clear();
        onAttributeChanged();
    }

    protected CompoundNBT getCapNBT() {
        CompoundNBT cap = stack.getOrCreateTagElement("cap");

        if (cap.contains("attribute"))
            return cap.getCompound("attribute");
        else {
            CompoundNBT nbt = new CompoundNBT();
            cap.put("attribute", nbt);
            return nbt;
        }
    }

    protected void setCapNBT(CompoundNBT nbt) {
        CompoundNBT cap = stack.getOrCreateTagElement("cap");
        cap.put("attribute", nbt);
    }

    protected void deserialize(CompoundNBT cap) {
        attribute.clear();
        ListNBT listNBT;

        if (!cap.contains("attribute")){
            listNBT = new ListNBT();
            cap.put("attribute", listNBT);
        }

        listNBT = cap.getList("attribute", Constants.NBT.TAG_COMPOUND);

        for (int i = 0, capNBTSize = listNBT.size(); i < capNBTSize; i++) {
            CompoundNBT compound = listNBT.getCompound(i);

            if (!compound.contains("id") || !compound.contains("modifier"))
                continue;

            Attribute attribute = ForgeRegistries.ATTRIBUTES.getValue(new ResourceLocation(compound.getString("id")));
            ListNBT list = compound.getList("modifier", Constants.NBT.TAG_COMPOUND);

            for (int j = 0, listSize = list.size(); j < listSize; j++)
                this.attribute.put(attribute, AttributeModifier.load(list.getCompound(j)));
        }
    }

    protected CompoundNBT serialize() {
        ListNBT listNBT = new ListNBT();

        attribute.asMap().forEach((a, m) -> {
            CompoundNBT nbt = new CompoundNBT();
            listNBT.add(nbt);
            //保存属性id
            nbt.putString("id", Objects.requireNonNull(a.getRegistryName()).toString());
            ListNBT modifierList = new ListNBT();
            //保存属性操作符
            nbt.put("modifier", modifierList);
            m.forEach(modifier -> modifierList.add(modifier.save()));
        });

        CompoundNBT nbt = new CompoundNBT();
        nbt.putBoolean("latest", false);
        nbt.put("attribute", listNBT);
        return nbt;
    }

    protected void onAttributeChanged() {
    }

    private void clientCheck() {
        if (EffectiveSide.get().isServer())
            return;

        CompoundNBT nbt = getCapNBT();

        if (!nbt.contains("latest") || nbt.getBoolean("latest"))
            return;

        reload();
        getCapNBT().putBoolean("latest", true);
    }

    protected void reload() {
        deserialize(getCapNBT());
    }
}
